#include <functional>
#include <unistd.h>
#include <pthread.h>
#include <vector>
#include <iostream>
#include <sys/types.h>
#include <string.h>
#include <signal.h>
#include "LockGuard.hpp"

using namespace std;

int ticket = 1000;
// 定义全局锁
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
// 全局使用锁
void *ThreadTicketGlobal(void *args)
{
    pthread_detach(pthread_self());
    // 给每个线程进行死循环抢票
    while (1)
    {
        // 每个线程访问临界资源时，一定需要加锁，将并行转化为串行
        // 任何时刻只允许一线程成功申请锁！多个线程申请锁失败，需要在mutex中进行等待!
        pthread_mutex_lock(&mutex);

        const char *name = (const char *)args;
        // 当加锁后，线程也是可以进行线程切换的，
        // 而切换时加锁可以保证其他线程无法访问。

        if (ticket > 0)
        {
            usleep(1000);
            printf("%s get a ticket: %d\n", name, --ticket);
            pthread_mutex_unlock(&mutex);
        }
        else
        {
            pthread_mutex_unlock(&mutex);
            break;
        }
    }
}
// 在ThreadTicketGlobal的基础上我们封装了锁
void *ThreadTicketGlobalWithGuard(void *args)
{
    pthread_detach(pthread_self());
    while (1)
    {
        // 原理：临时变量出了当前循环就会释放！！！
        LockGuard guard(&mutex);
        const char *name = (const char *)args;
        if (ticket > 0)
        {
            usleep(1000);
            printf("%s get a ticket: %d\n", name, --ticket);
        }
        else
        
        {
            break;
        }
    }

    // 临界区！如果需要对其他模块进行锁的区分
    // 我们可以通过{ }定义代码域，确定锁的声明周期
    
    // 前面不受影响
    {
        LockGuard guard(&mutex);
        // 后续代码
    }
    // 后面不受影响
}
// 锁的全局使用
void test1()
{
    pthread_t tid1;
    pthread_create(&tid1, nullptr, ThreadTicketGlobal, (void *)"Thread 1");
    pthread_t tid2;
    pthread_create(&tid2, nullptr, ThreadTicketGlobal, (void *)"Thread 2");
    pthread_t tid3;
    pthread_create(&tid3, nullptr, ThreadTicketGlobal, (void *)"Thread 3");
    pthread_t tid4;
    pthread_create(&tid4, nullptr, ThreadTicketGlobal, (void *)"Thread 4");

    while (1)
    {
        if (ticket <= 0)
        {
            cout << "主线程结束抢票" << endl;
            sleep(5);
            cout << "ticket num = " << ticket << endl;
            break;
        }
    }
}

template <class T>
class ThreadObject
{
public:
    ThreadObject(const T &data, pthread_mutex_t *lock)
        : _data(data), _mutex_lock(lock)
    {
    }

    T GetData() const { return _data; }
    pthread_mutex_t *GetLock() const { return _mutex_lock; }

private:
    T _data;
    pthread_mutex_t *_mutex_lock;
};
// 局部使用锁
void *ThreadTicketPart(void *args)
{
    ThreadObject<string> *td = (ThreadObject<string> *)args;
    // 获取对象内部的锁
    pthread_mutex_t *obj_lock = td->GetLock();
    // 获取对象的name
    const char *name = td->GetData().c_str();

    pthread_detach(pthread_self());
    // 给每个线程进行死循环抢票
    while (1)
    {
        // 每个线程访问临界资源时，一定需要加锁，将并行转化为串行
        // 任何时刻只允许一线程成功申请锁！多个线程申请锁失败，需要在mutex中进行等待!
        pthread_mutex_lock(obj_lock);

        // 当加锁后，线程也是可以进行线程切换的，
        // 而切换时加锁可以保证其他线程无法访问。

        if (ticket > 0)
        {
            usleep(1000);
            printf("%s get a ticket: %d\n", name, --ticket);
            pthread_mutex_unlock(obj_lock);
        }
        else
        {
            pthread_mutex_unlock(obj_lock);
            break;
        }
    }
}
// 封装锁
void *ThreadTicketPartWithGuard(void *args)
{
    ThreadObject<string> *td = (ThreadObject<string> *)args;
    // 获取对象内部的锁
    pthread_mutex_t *obj_lock = td->GetLock();
    // 获取对象的name
    const char *name = td->GetData().c_str();

    pthread_detach(pthread_self());
    // 给每个线程进行死循环抢票
    while (1)
    {
        LockGuard guard(obj_lock);

        if (ticket > 0)
        {
            usleep(1000);
            printf("%s get a ticket: %d\n", name, --ticket);
        }
        else
        {
            break;
        }
    }
}
// 锁的局部使用|传入对象参数
void test3()
{
    pthread_mutex_t mutex_lock;
    pthread_mutex_init(&mutex_lock, nullptr);

    ThreadObject<string> *td1 = new ThreadObject<string>("Thread 1", &mutex_lock);
    ThreadObject<string> *td2 = new ThreadObject<string>("Thread 2", &mutex_lock);
    ThreadObject<string> *td3 = new ThreadObject<string>("Thread 3", &mutex_lock);
    ThreadObject<string> *td4 = new ThreadObject<string>("Thread 4", &mutex_lock);

    pthread_t tid1;
    pthread_create(&tid1, nullptr, ThreadTicketPart, (void *)td1);
    pthread_t tid2;
    pthread_create(&tid2, nullptr, ThreadTicketPart, (void *)td2);
    pthread_t tid3;
    pthread_create(&tid3, nullptr, ThreadTicketPart, (void *)td3);
    pthread_t tid4;
    pthread_create(&tid4, nullptr, ThreadTicketPart, (void *)td4);

    while (1)
    {
        if (ticket <= 0)
        {
            cout << "主线程结束抢票" << endl;
            sleep(5);
            cout << "ticket num = " << ticket << endl;
            break;
        }
    }
    pthread_mutex_destroy(&mutex_lock);
}

// 多线程运行下，会出现个别线程抢夺锁的能力过强
// 导致其他线程无法获得锁，即为 线程饥饿 

// 为了解决饥饿问题，我们需要让线程运行具有一定顺序，即实现线程同步

int main()
{
    test3();
}